import es
import cmdlib

from wcs.libs.extensions import addons
from wcs.libs.constants import *

__all__ = ["commands", "CommandObject"]

class ServerCommandManager(object):
    """
    This object is used to register and unregister server, say and client
    commands. We will use this to manage all the commands and the relative
    callback functions.
    """
    def __init__(self):
        """
        Default constructor, register all commands and initialise all variables.
        """
        self.commands = {}
        self.registerCommand("wcs", "The main WCS command", self._mainCommand,
                                                                      "server")
        self.registerCommand("wcs", "The main WCS command", self._mainSayCmd,
                                               ("say", "console"))
        self.registerCommand("changerace", "Change the registered race",
                             self.changeRace,  ("say", "console"))
        self.registerCommand("raceinfo", "Change the registered race",
                             self.changeRace,  ("say", "console"))
        self.registerCommand("spendskills", "Spend your skill points",
                             self.spendSkills, ("say", "console"))
        self.registerCommand("shopmenu", "Shows the shop menu",
                             self.shopMenu, ("say", "console"))
        self.registerCommand("shopinfo", "SHows the shop menu",
                             self.shopMenu, ("say", "console"))
        self.registerCommand("+ultimate", "Start the ultimate command",
                             self.ultimateOn,  "console")
        self.registerCommand("-ultimate", "Stop the ultimate command",
                             self.ultimateOff, "console")
        for i in xrange(1, 6):
            if i == 1:
                i = ""
            self.registerCommand("+ability%s" % i, "Starts the ability command",
                                 self.abilityOn, "console")
            self.registerCommand("-ability%s" % i, "Stops the ability command",
                                 self.abilityOff, "console")
        self.registerCommand("ultimate", "Toggle the status of the ultimate",
                             self.toggleUltimate, "console")
        self.registerCommand("!ultimate", "Toggle the status of the ultimate",
                             self.toggleUltimate, "say")

    def __del__(self):
        """
        Default destructor, ensure that all commands clear and therefor their
        deconstructor executes unregistering all commands.
        """
        self.clearCommands()

    def clearCommands(self):
        """
        ensure that all commands clear and therefor their deconstructor executes
        unregistering all commands.
        """
        for commandObject in self.commands.itervalues():
            commandObject.unregister()

    def registerCommand(self, name, description, callback, types):
        """
        Executed when we wish to register a new command. This will enable us
        to have functions executed when specific commands are accessed on either
        a say, client or server command.

        @param string name The name of the command, this is the actual 'command'
        @param string description A short description of the effect
        @param function callback A function address to be used as a callback
        @param iterable types A list of types that the command will be register
                              under; either console, server or say commands.
        """
        if not hasattr(types, "__iter__"):
            types = (types,)
        if not callable(callback):
            raise AttributeError("Callback for command '%s' is not callable" %
                                    name)
        if name in self.commands:
            newCommandObject = CommandObject(name, description, callback, types)
            if hasattr(self.commands[name], "__iter__"):
                self.commands[name].append(newCommandObject)
            else:
                self.commands[name] = [self.commands[name], newCommandObject]
        else:
            self.commands[name] = CommandObject(name, description, callback, 
                                                                   types)

    def unregisterCommand(self, name):
        """
        Removes the CommandObject instance from memory and in turn allowing the
        deconstructor to unregister the say command.

        @param string name The name of the command to unregister.
        """
        del self.commands[name]

    def _mainCommand(self, userid, args):
        """
        Executed when the command 'wcs' is executed. Parse any sub commands
        and execute their following actions
        """
        from wcs.libs.races import races
        from wcs.libs.races.groups import raceGroups
        
        if userid is not None:
            """ No client or say commands """
            return
        if not args:
            raise ValueError("Expected command 'wcs' to contain sub commands,"
                          " use \"wcs help\" to obtain help about the command")
        subCommand = args[0]
        if subCommand == "load":
            races.loadRace(args[1], args[2:] if len(args) >= 3 else None)

        elif subCommand == "loadallraces":
            for folder in RACE_PATH.dirs():
                raceName = folder.splitpath()[1]
                if folder.joinpath(raceName + ".py").exists():
                    races.loadRace(raceName)

        elif subCommand == "loadaddon":
            addons.loadExtension(args[1])

        elif subCommand == "loadalladdons":
            for folder in EXTENSION_PATH.dirs():
                extensionName = folder.splitpath()[1]
                if folder.joinpath(extensionName + ".py").exists():
                    addons.loadExtension(extensionName)
                    
        elif subCommand == "loaditem":
            itemName = args[1]
            itemPath = SHOP_PATH.joinpath(itemName).joinpath(itemName + ".py")
            if itemPath.exists():
                es.load("wcs/shopmenu/%s" % itemName)
            else:
                es.dbgmsg(0, "[WCS] Cannot find shop item '%s'" % itemName)
                
        elif subCommand == "loadallitems":
            for folder in SHOP_PATH.dirs():
                itemName = folder.splitpath()[1]
                if folder.joinpath(itemName + ".py").exists():
                    es.load("wcs/shopmenu/%s" % itemName)

        elif subCommand == "racegroup":
            if len(args) < 4:
                raise ValueError("Expected command 'wcs racegroup edit' should"
                  "contain 4 args: wcs racegroup edit <group> [group n] <auth>")
            if args[1] == "edit":
                authorization = args[-1]
                groups = args[2:-1]
                lastGroup = raceGroups
                for group in groups:
                    lastGroup = lastGroup.groups[group]
                lastGroup.authorization = authorization
        else:
            es.dbgmsg(0, "[WCS] Unkown subcommand for 'wcs' (%s)" %  subCommand)
            
    def _mainSayCmd(self, userid, args):
        """
        Executed when the user says the main say command in the chat or console.
        Ensure that we simply build the popup for the user and send it to them.
        
        @param int userid The ID of the user who executed the command.
        @param list args Any additional args after the command
        """
        from wcs.libs.commands.popups import mainPopup
        
        mainPopup.send(userid)

    def changeRace(self, userid, args):
        """
        Executed when a user types 'changerace' in console. Send a list of races
        to the player. When they select a race, information will display and
        gives them the ability to change to that race.
        """
        from wcs.libs.races.groups import raceGroups
        
        raceGroups.popup.send(userid)

    def spendSkills(self, userid, args):
        """
        Executed when a user types 'spendskills' in console or chat. Send the
        list of skills that their current race has to the player. This will
        allow them to upgrade their skills.
        """
        from wcs.libs.players import players
        from wcs.libs.races import races
        
        player = players[userid]
        if player is not None:
            races[player.currentRace].skillsPopup.send(userid)
            
    def shopMenu(self, userid, args):
        """
        Exexcuted when a user types 'shopinfo' or 'shopmenu' in console or chat.
        Build them an item menu and send them the popup instance.
        """
        from wcs.libs.items.popups import buildItemPopup
        
        buildItemPopup().send(userid)
            
    def ultimateOn(self, userid, args):
        """
        Executed when the user presses their console bind for the ultimate key.
        This executes whilst the key is pressed down. Simply fire the ultimate
        on key if their cooldown is still valid.
        
        @param int userid The ID of the user who pressed the commnad
        @param tuple args Any additional args sent with the command
        """
        from wcs.libs.players import players
        from wcs.libs.races import races
        from wcs.libs.skills import Ultimate
        from wcs.libs.events import Event
        from wcs.libs.messages import messages
        
        if es.getplayerprop(userid, "CBasePlayer.pl.deadflag"):
            messages.send(userid, "ultimate dead")
            return
        
        player = players[userid]
        if player["blockUltimate"] is True:
            messages.send(userid, "ultimate blocked")
            return
        
        player = players[userid]
        race = player[player.currentRace]
        raceObject = races[player.currentRace]
        for skill in raceObject:
            if isinstance(skill, Ultimate):
                coolDown = race[skill.name].coolDown
                if coolDown:
                    tokens = {}
                    tokens["time"] = int(coolDown) + 1
                    messages.send(userid, "ultimate cool down", tokens)
                else:
                    event = Event("player_ultimate_on")
                    event["userid"] = userid
                    event.fire()
    
    def ultimateOff(self, userid, args):
        """
        Executed when the user releases the key to the ultimate bind. This fires
        anothuer custom event notifying the race scripts that the ultimate key
        has been released.
        
        @param int userid The ID of the user who released the ultimate key
        @param tuple args Any additional arguments passed with the command
        """
        from wcs.libs.players import players
        from wcs.libs.races import races
        from wcs.libs.skills import Ultimate
        from wcs.libs.events import Event
        
        if es.getplayerprop(userid, "CBasePlayer.pl.deadflag"):
            return
        
        player = players[userid]
        if player["blockUltimate"] is True:
            return
        
        player = players[userid]
        race = player[player.currentRace]
        raceObject = races[player.currentRace]
        for skill in raceObject:
            if isinstance(skill, Ultimate):
                coolDown = race[skill.name].coolDown
                event = Event("player_ultimate_off")
                event["userid"] = userid
                event["cooldown"] = coolDown
                event.fire()
                
    def toggleUltimate(self, userid, args):
        """
        Executed whenever the player pressed the 'ultimate' console bind. This
        is to allow player's to keep their binds for the old WCS mod as well.
        Pressing the ultimate key once will activate the ultimate, and again
        will end the ultimate.
        
        @param int userid The ID of the user who pressed the console key bind
        @param tuple args Any additional args passed with the command
        """
        from wcs.libs.players import players
        from wcs.libs.races import races
        from wcs.libs.skills import Ultimate
        from wcs.libs.events import Event
        from wcs.libs.messages import messages
        
        if es.getplayerprop(userid, "CBasePlayer.pl.deadflag"):
            messages.send(userid, "ultimate dead")
            return
        
        player = players[userid]
        if player["blockUltimate"] is True:
            messages.send(userid, "ultimate blocked")
            return
        
        player = players[userid]
        race = player[player.currentRace]
        raceObject = races[player.currentRace]
        for skill in raceObject:
            if isinstance(skill, Ultimate):
                coolDown = race[skill.name].coolDown
                if player["toggleUltimate"]:
                    event = Event("player_ultimate_off")
                    event["cooldown"] = coolDown
                    event["userid"] = userid
                    event.fire()
                else:
                    if coolDown:
                        tokens = {}
                        tokens["time"] = coolDown
                        messsages.send(userid, "ultimate cool down", tokens)
                    else:
                        event = Event("player_ultimate_on")
                        event["userid"] = userid
                        event.fire()
                player["toggleUltimate"] = not player["toggleUltimate"]
    
    def abilityOn(self, userid, args):
        """
        Executed when the +ability[number] key has been pressed. Retrieve the
        ability number which has been pressed, do some santity checks to ensure
        that that particular ability is fine to process, then fire the event.
        
        @param int userid The ID of the user who executed the command
        @param list args A list of arguments following the command
        """
        from wcs.libs.events import Event
        from wcs.libs.messages import messages
        from wcs.libs.players import players
        
        if es.getplayerprop(userid, "CBasePlayer.pl.deadflag"):
            messages.send(userid, "ability dead")
            return
        
        player = players[userid]
        if player["blockAbility"] is True:
            messages.send(userid, "ability blocked")
            return
        
        abilityNumber = es.getargv(0).replace("+ability", "", 1)
        if not es.getplayerprop(userid, "CBasePlayer.pl.deadflag"):
            if abilityNumber:
                event = Event("player_ability_on%s" % abilityNumber)
            else:
                event = Event("player_ability_on")
            event["userid"] = userid
            event.fire()
    
    def abilityOff(self, userid, args):
        """
        Executed when the -ability[number] key has been pressed. Retrieve the
        ability number which has been pressed, do some santity checks to ensure
        that that particular ability is fine to process, then fire the event.
        
        @param int userid The ID of the user who executed the command
        @param list args A list of arguments following the command
        """
        from wcs.libs.events import Event
        from wcs.libs.players import players
        
        if es.getplayerprop(userid, "CBasePlayer.pl.deadflag"):
            return
        
        player = players[userid]
        if player["blockAbility"] is True:
            return
        
        abilityNumber = es.getargv(0).replace("-ability", "", 1)
        if not es.getplayerprop(userid, "CBasePlayer.pl.deadflag"):
            if abilityNumber:
                event = Event("player_ability_off%s" % abilityNumber)
            else:
                event = Event("player_ability_off")
            event["userid"] = userid
            event.fire()

class CommandObject(object):
    def __init__(self, name, description, callback, types):
        """
        Default constructor, register the variables with the command library
        and provide functionality to them.

        @param string name The name of the command
        @param string description A short description explaining functionality
        @param function callback The command callback
        @param iterable types A list of types that the command will be register
                              under; either console, server or say commands.
        """
        self.types = types
        self.callback = callback
        self.name = name
        if "server" in types:
            cmdlib.registerServerCommand(name, self._callback, description)
        if "say" in types:
            cmdlib.registerSayCommand(name, self._callback, description)
        if "console" in types:
            cmdlib.registerClientCommand(name, self._callback, description)
        self.registered = True

    def __del__(self):
        """
        Default deconstructor, unregister all commands which were assigned.
        """
        if self.registered:
            self.unregister()

    def unregister(self):
        if self.registered is True:
            if "server" in self.types:
                cmdlib.unregisterServerCommand(self.name)
            if "say" in self.types:
                cmdlib.unregisterSayCommand(self.name)
            if "console" in self.types:
                cmdlib.unregisterClientCommand(self.name)
            self.registered = False

    def _callback(self, *args):
        """
        A callback function used to interpret the callbacks from the command
        library and the actual callback. This ensures that the userid parameter
        is passed regardless of which method called the command. This ensures
        that we can use the same 2 parameters for all 3 types of commands rather
        than creating 2 different functions accepting different parameters.
        """
        if len(args) == 2:
            userid, args = args
        else:
            userid = None
            args = args[0]
        self.callback(userid, args)
        
commands = ServerCommandManager()